package com.prp.algorithm;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;

import com.prp.bo.SNSConstants;
import com.prp.db.DAOFactory;
import com.prp.db.idao.IArtificialSubjectRecommendationRecordDAO;
import com.prp.db.idao.INeighbourRelationshipDAO;
import com.prp.db.idao.ISearchRecordDAO;
import com.prp.db.idao.ISubjectCategoryDAO;
import com.prp.db.idao.ISubjectChildCategoryDAO;
import com.prp.db.idao.ISubjectCommentDAO;
import com.prp.db.idao.ISubjectRecommendationDAO;
import com.prp.db.idao.IUserInformationDAO;
import com.prp.db.po.ArtificialSubjectRecommendationRecord;
import com.prp.db.po.SearchRecord;
import com.prp.db.po.SubjectCategory;
import com.prp.db.po.SubjectChildCategory;
import com.prp.db.po.SubjectComment;
import com.prp.db.po.SubjectRecommendation;
import com.prp.db.po.UserInformation;

public class NeighboursRecommend {

	private static HashMap<Integer, UserInfo> userMap = new HashMap<Integer, UserInfo>();

	/**
	 * 输入一个用户id，返回自动推荐的相关用户id的列表。
	 * 
	 * @param userId
	 * @return
	 */
	public synchronized static ArrayList<Integer> recommend(int userId) {
		if (!getUsersToRecommand(userId)) {
			System.out.println("User " + userId + " is not exist!");
			return null;
		}

		ArrayList<RecommendInfo> userList = new ArrayList<RecommendInfo>();
		for (Iterator<UserInfo> it = userMap.values().iterator(); it.hasNext();) {
			UserInfo user = it.next();
			if (user.user_id == userId)
				continue;
			RecommendInfo tempReInfo = new RecommendInfo();
			tempReInfo.user_id = userId;
			tempReInfo.r_user_id = user.user_id;
			tempReInfo.similarity_value = calSimilarity(userMap.get(userId),
					user);
			tempReInfo.detail_similarity_value = calDetailSimilarity(userMap
					.get(userId), user);
			userList.add(tempReInfo);
		}

		userMap.clear();

		Collections.sort(userList, new java.util.Comparator<RecommendInfo>() {
			public int compare(RecommendInfo o1, RecommendInfo o2) {
				return (o1.similarity_value < o2.similarity_value) ? 1
						: ((o1.similarity_value > o2.similarity_value) ? -1
								: ((o1.detail_similarity_value < o2.detail_similarity_value) ? 1
										: ((o1.detail_similarity_value > o2.detail_similarity_value) ? -1
												: 0)));
			}
		});

		ArrayList<Integer> idList = new ArrayList<Integer>();
		for (int i = 0; i < userList.size(); i++)
			idList.add(userList.get(i).r_user_id);
		return idList;
	}

	/**
	 * 获取推荐候选人列表
	 * 
	 * @param userId
	 * @return
	 */
	public synchronized static boolean getUsersToRecommand(int userId) {
		DAOFactory factory = DAOFactory.getDAOFactory();
		IUserInformationDAO userDAO = factory.getUserInformationDAO();
		UserInformation mainUser = userDAO.findById(userId);
		if (mainUser == null)
			return false;

		INeighbourRelationshipDAO nrDAO = factory.getNeighbourRelationshipDAO();
		ArrayList<UserInformation> userList = (ArrayList<UserInformation>) userDAO
				.findByCommunityId(mainUser.getCommunityId());
		// userList.addAll(nrDAO.findByUserId(mainUser.getUserId()));

		getUserMap(factory, userList, mainUser);

		if (userMap.size() <= SNSConstants.MIN_NEIGHBOURS_RECOMMEND_USER_SIZE) {
			ArrayList<UserInformation> tempUserList = (ArrayList<UserInformation>) nrDAO
					.findByUserId(mainUser.getUserId());
			for (int i = 0; i < tempUserList.size(); i++) {
				userList.addAll(userDAO.findByCommunityId(tempUserList.get(i)
						.getCommunityId()));
			}
			getUserMap(factory, userList, mainUser);
		}

		// 如果得到的邻友推荐数小于约定值，则
		if (userMap.size() <= SNSConstants.MIN_NEIGHBOURS_RECOMMEND_USER_SIZE) {
			userList.clear();
			// ????????????????????????????????????????????(扩大推荐候选范围)
			ArrayList<UserInformation> tempList = (ArrayList<UserInformation>) userDAO
					.findAll();
			for (int i = 0; i < tempList.size(); i++) {
				if (tempList.size() - i <= SNSConstants.MIN_NEIGHBOURS_RECOMMEND_USER_SIZE
						- userList.size())
					userList.add(tempList.get(i));
				else if (Math.random() < 0.13)
					userList.add(tempList.get(i));
			}
			userList.addAll(userDAO.findAll());
			getUserMap(factory, userList, mainUser);
		}

		return true;
	}

	/**
	 * 获取候选推荐用户列表，并计算他们的兴趣值
	 * 
	 * @param factory
	 * @param mainUser
	 */
	public synchronized static void getUserMap(DAOFactory factory,
			ArrayList<UserInformation> userList, UserInformation mainUser) {
		INeighbourRelationshipDAO nrDAO = factory.getNeighbourRelationshipDAO();

		ISubjectCategoryDAO subcDAO = factory.getSubjectCategoryDAO();
		ArrayList<SubjectCategory> subcList = (ArrayList<SubjectCategory>) subcDAO
				.findAll();

		ISubjectChildCategoryDAO subccDAO = factory
				.getSubjectChildCategoryDAO();
		ArrayList<SubjectChildCategory> subccList = (ArrayList<SubjectChildCategory>) subccDAO
				.findAll();

		for (int index = 0; index < userList.size(); index++) {
			UserInformation tempInfo = userList.get(index);

			if (nrDAO.isNeighbour(tempInfo.getUserId(), mainUser.getUserId()))
				continue;

			UserInfo tempUser = userMap.get(tempInfo.getUserId());
			if (tempUser != null)
				continue;

			tempUser = new UserInfo();
			tempUser.user_id = tempInfo.getUserId();

			// 给用户添加子类别同样数目的mark？？？？？？？？？？？？？？(List初始化，在调用时需要。稍后该方法将整合，将下面的四个计算信息整合成一个调用
			// ，将此操作隐藏。)
			for (int i = 0; i < subcList.size(); i++)
				tempUser.mark_list.add(0.00);
			for (int i = 0; i < subccList.size(); i++)
				tempUser.detail_mark_list.add(0.00);

			calSearchInfo(factory, subcList, subccList, tempUser);

			calSubjectPublish(factory, subcList, subccList, tempUser);
			calSubjectRecommendation(factory, subcList, subccList, tempUser);
			calSubjectComment(factory, subcList, subccList, tempUser);

			userMap.put(tempUser.user_id, tempUser);
		}
	}

	/**
	 * 通过搜索记录，计算用户兴趣值系数
	 * 
	 * @param factory
	 * @param subcList
	 * @param subccList
	 * @param tempUser
	 */
	public synchronized static void calSearchInfo(DAOFactory factory,
			ArrayList<SubjectCategory> subcList,
			ArrayList<SubjectChildCategory> subccList, UserInfo tempUser) {
		ISubjectCategoryDAO subcDAO = factory.getSubjectCategoryDAO();
		ISubjectChildCategoryDAO subccDAO = factory
				.getSubjectChildCategoryDAO();
		ISearchRecordDAO searchDAO = factory.getSearchRecordDAO();
		ArrayList<SearchRecord> searchList = (ArrayList<SearchRecord>) searchDAO
				.findBySearchUserId(tempUser.user_id);
		for (int i = 0; i < searchList.size(); i++) {
			// 子类别index
			int dmIndex = subccList.indexOf(subccDAO.findById(searchList.get(i)
					.getCategory()));
			// 类别index
			int mIndex = subcList.indexOf(subcDAO.findById(subccList.get(
					dmIndex).getCategroyId()));
			tempUser.detail_mark_list.set(dmIndex, tempUser.detail_mark_list
					.get(dmIndex)
					+ searchList.get(i).getSearchFreq()
					* SNSConstants.SEARCH_RECORD);
			// ?????????????????????????????????????
			// 加上去不清0吗？

			tempUser.mark_list.set(mIndex, tempUser.mark_list.get(mIndex)
					+ searchList.get(i).getSearchFreq()
					* SNSConstants.SEARCH_RECORD);
		}
	}

	/**
	 * 通过用户发帖情况，计算用户兴趣值系数
	 * 
	 * @param factory
	 * @param subcList
	 * @param subccList
	 * @param tempUser
	 */
	public synchronized static void calSubjectPublish(DAOFactory factory,
			ArrayList<SubjectCategory> subcList,
			ArrayList<SubjectChildCategory> subccList, UserInfo tempUser) {
		ISubjectCategoryDAO subcDAO = factory.getSubjectCategoryDAO();
		ISubjectChildCategoryDAO subccDAO = factory
				.getSubjectChildCategoryDAO();
		ISubjectRecommendationDAO subRecDAO = factory
				.getSubjectRecommendationDAO();
		ArrayList<SubjectRecommendation> subRecList = (ArrayList<SubjectRecommendation>) subRecDAO
				.findByPublishId(tempUser.user_id);
		for (int i = 0; i < subRecList.size(); i++) {
			int dmIndex = subccList.indexOf(subccDAO.findById(subRecList.get(i)
					.getCategoryChildId()));
			int mIndex = subcList.indexOf(subcDAO.findById(subccList.get(
					dmIndex).getCategroyId()));
			tempUser.detail_mark_list.set(dmIndex, tempUser.detail_mark_list
					.get(dmIndex)
					+ SNSConstants.PUBLISH_RECORD);
			tempUser.mark_list.set(mIndex, tempUser.mark_list.get(mIndex)
					+ SNSConstants.PUBLISH_RECORD);
		}
	}

	/**
	 * 通过用户向他人推荐主题贴情况计算兴趣值系数，需要用户评分系数参与计算
	 * 
	 * @param factory
	 * @param subcList
	 * @param subccList
	 * @param tempUser
	 */
	public synchronized static void calSubjectRecommendation(
			DAOFactory factory, ArrayList<SubjectCategory> subcList,
			ArrayList<SubjectChildCategory> subccList, UserInfo tempUser) {
		ISubjectCategoryDAO subcDAO = factory.getSubjectCategoryDAO();
		ISubjectChildCategoryDAO subccDAO = factory
				.getSubjectChildCategoryDAO();
		ISubjectRecommendationDAO subRecDAO = factory
				.getSubjectRecommendationDAO();
		IArtificialSubjectRecommendationRecordDAO asrrDAO = factory
				.getArtificialSubjectRecommendationRecordDAO();
		ISubjectCommentDAO scDAO = factory.getSubjectCommentDAO();
		ArrayList<ArtificialSubjectRecommendationRecord> asrrList = (ArrayList<ArtificialSubjectRecommendationRecord>) asrrDAO
				.findByRecommFromId(tempUser.user_id);
		ArrayList<SubjectComment> scList = (ArrayList<SubjectComment>) scDAO
				.findByCommentUserId(tempUser.user_id);
		for (int i = 0; i < asrrList.size(); i++) {
			int dmIndex = subccList.indexOf(subccDAO.findById(subRecDAO
					.findById(asrrList.get(i).getReommArticleId())
					.getCategoryChildId()));
			int mIndex = subcList.indexOf(subcDAO.findById(subccList.get(
					dmIndex).getCategroyId()));
			int score = SNSConstants.MAX_SCORE_RECORD / 2;
			int j;
			for (j = 0; j < scList.size(); j++)
				if (scList.get(j).getToSubjectId() == asrrList.get(i)
						.getReommArticleId())
					break;
			if (j < scList.size())
				if (scList.get(j).getToSubjectId() == asrrList.get(i)
						.getReommArticleId())
					score = scList.get(j).getScore();
			double coefficient = score / SNSConstants.MAX_SCORE_RECORD * 2;
			tempUser.detail_mark_list.set(dmIndex, tempUser.detail_mark_list
					.get(dmIndex)
					+ coefficient * SNSConstants.RECOMMAND_RECORD);
			tempUser.mark_list.set(mIndex, tempUser.mark_list.get(mIndex)
					+ coefficient * SNSConstants.RECOMMAND_RECORD);
		}
	}

	/**
	 * 通过对主题贴的评论计算用户兴趣值系数，需要用户评分系数参与计算
	 * 
	 * @param factory
	 * @param subcList
	 * @param subccList
	 * @param tempUser
	 */
	public synchronized static void calSubjectComment(DAOFactory factory,
			ArrayList<SubjectCategory> subcList,
			ArrayList<SubjectChildCategory> subccList, UserInfo tempUser) {
		ISubjectCategoryDAO subcDAO = factory.getSubjectCategoryDAO();
		ISubjectChildCategoryDAO subccDAO = factory
				.getSubjectChildCategoryDAO();
		ISubjectRecommendationDAO subRecDAO = factory
				.getSubjectRecommendationDAO();
		ISubjectCommentDAO subComDAO = factory.getSubjectCommentDAO();
		ArrayList<SubjectComment> subComList = (ArrayList<SubjectComment>) subComDAO
				.findByCommentUserId(tempUser.user_id);
		for (int i = 0; i < subComList.size(); i++) {
			int dmIndex = subccList.indexOf(subccDAO.findById(subRecDAO
					.findById(subComList.get(i).getToSubjectId())
					.getCategoryChildId()));
			int mIndex = subcList.indexOf(subcDAO.findById(subccList.get(
					dmIndex).getCategroyId()));
			double coefficient = subComList.get(i).getScore()
					/ SNSConstants.MAX_SCORE_RECORD * 2;
			tempUser.detail_mark_list.set(dmIndex, tempUser.detail_mark_list
					.get(dmIndex)
					+ coefficient * SNSConstants.COMMENT_RECORD);
			tempUser.mark_list.set(mIndex, tempUser.mark_list.get(mIndex)
					+ coefficient * SNSConstants.COMMENT_RECORD);
		}
	}

	/**
	 * 计算用户兴趣值相似度
	 * 
	 * @param user1
	 * @param user2
	 * @return
	 */
	public synchronized static double calSimilarity(UserInfo user1,
			UserInfo user2) {
		// ???????????????????
		if (user1.mark_list.size() != user2.mark_list.size())
			return 0;
		double vector_mul = 0.0000, user1_mold = 0.0000, user2_mold = 0.0000;
		for (int index = 0; index < user1.mark_list.size(); index++) {
			vector_mul += user1.mark_list.get(index)
					* user2.mark_list.get(index);
			user1_mold += user1.mark_list.get(index)
					* user1.mark_list.get(index);
			user2_mold += user2.mark_list.get(index)
					* user2.mark_list.get(index);
		}
		double cos_value = vector_mul
				/ (Math.sqrt(user1_mold) * Math.sqrt(user2_mold));
		return cos_value;
	}

	/**
	 * 计算两个用户兴趣值细分后相似度
	 * 
	 * @param user1
	 * @param user2
	 * @return
	 */
	public synchronized static double calDetailSimilarity(UserInfo user1,
			UserInfo user2) {
		if (user1.detail_mark_list.size() != user2.detail_mark_list.size())
			return 0;
		double vector_mul = 0.0000, user1_mold = 0.0000, user2_mold = 0.0000;
		for (int index = 0; index < user1.detail_mark_list.size(); index++) {
			vector_mul += user1.detail_mark_list.get(index)
					* user2.detail_mark_list.get(index);
			user1_mold += user1.detail_mark_list.get(index)
					* user1.detail_mark_list.get(index);
			user2_mold += user2.detail_mark_list.get(index)
					* user2.detail_mark_list.get(index);
		}
		double cos_value = vector_mul
				/ (Math.sqrt(user1_mold) * Math.sqrt(user2_mold));
		return cos_value;
	}
}
